package com.inspur.edp.cef.designtime.api.entity.commonstructure;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultCollectionType;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultEnumerationItem;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultEnumerationType;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultPrimitiveType;
import com.inspur.edp.caf.cef.schema.base.element.impl.DefaultProperty;
import com.inspur.edp.caf.cef.schema.datatype.DataType;
import com.inspur.edp.caf.cef.schema.datatype.EnumerationType;
import com.inspur.edp.caf.cef.schema.datatype.PrimitiveTypeKind;
import com.inspur.edp.caf.cef.schema.datatype.StructuredType;
import com.inspur.edp.caf.cef.schema.element.Property;
import com.inspur.edp.caf.cef.schema.structure.CommonStructure;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspEnumValueCollection;
import com.inspur.edp.cef.designtime.api.element.EnumIndexType;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.designtime.api.entity.GspCommonDataType;
import com.inspur.edp.cef.designtime.api.exceptions.CommonEntityErrorCodeEnum;
import com.inspur.edp.cef.designtime.api.exceptions.CommonEntityException;

import java.util.ArrayList;

/**
 * The Definition Of CefCommonStructureUtil
 *
 * @ClassName: CefCommonStructureUtil
 * @Author: Benjamin Gong
 * @Date: 2021/1/11 17:13
 * @Version: V1.0
 */
public abstract class CefCommonStructureUtil {
  // region abstract

  protected abstract CommonStructure getRefBeCommonStructure(GspAssociation asso);

  protected abstract CommonStructure getRefUdtCommonStructure(String udtId);

  // endregion

  public final DataType getCommonFieldDataType(IGspCommonField field) {
    if (field.getIsUdt()) {
      // 注意克隆
      CommonStructure udtCommonStructure = getRefUdtCommonStructure(field.getUdtID());
      return udtCommonStructure.getStructuredTypes().get(0);
    }

    switch (field.getObjectType()) {
      case Association:
        return getAssoElementType(field.getChildAssociations().get(0));
      case Enum:
        EnumerationType enumPropertyType = getEnumElementType(field);
        return enumPropertyType;
      default:
        return getBasePrimitiveType(field.getMDataType());
      // todo:DynamicProp暂不处理
    }
  }

  // region private
  private EnumerationType getEnumElementType(IGspCommonField field) {
    GspEnumValueCollection enumValues = field.getContainEnumValues();
    DefaultEnumerationType defaultEntityType = new DefaultEnumerationType();
    String prefix = getFieldEnumTypeName(field);
    defaultEntityType.setId(prefix);
    defaultEntityType.setCode(prefix);
    defaultEntityType.setName(prefix);
    defaultEntityType
        .setProperties(defaultEntityType.getProperties() == null ? new ArrayList<Property>() :
            defaultEntityType.getProperties());
    Boolean isIntegerIndex = field.getEnumIndexType().equals(EnumIndexType.Integer);
    // Index 整型/字符串
    defaultEntityType.getProperties().add(convertEnumValueToProperty(prefix, isIntegerIndex));
    defaultEntityType.setEnumerationItems(defaultEntityType.getEnumerationItems() == null ?
        new ArrayList<>() :
        defaultEntityType.getEnumerationItems());
    for (GspEnumValue enumValue : enumValues) {
      defaultEntityType.getEnumerationItems().add(convertEnumValueToItem(enumValue, prefix,
          isIntegerIndex));
    }
    return defaultEntityType;
  }

  private DataType getAssoElementType(GspAssociation asso) {
    CommonStructure refBeStructure = getRefBeCommonStructure(asso);
    StructuredType refBeStructureType = null;
    for (StructuredType item : refBeStructure.getStructuredTypes()) {
      if (item.getId().equals(getRefObjId(asso))) {
        refBeStructureType = item;
        break;
      }
    }
    ((GspCommonDataType) refBeStructureType)
        .setCode(this.getAssociationTypeName(asso.getBelongElement()));
    return refBeStructureType;
  }

  protected String getRefObjId(GspAssociation asso) {
    IGspCommonField belongElement = asso.getBelongElement();
    if (belongElement == null) {
      return asso.getRefObjectID();
    }

    return asso.getRefObjectID() + belongElement.getID();
  }

  private DataType getBasePrimitiveType(GspElementDataType dataType) {
    switch (dataType) {
      case Binary:
        DefaultCollectionType collectionType = new DefaultCollectionType();
        DefaultPrimitiveType collectionPrimitiveType = new DefaultPrimitiveType();
        collectionPrimitiveType.setPrimitiveTypeKind(getPrimitiveTypeKind(dataType));
        collectionType.setElementType(collectionPrimitiveType);
        return collectionType;
      default:
        DefaultPrimitiveType primitiveType = new DefaultPrimitiveType();
        primitiveType.setPrimitiveTypeKind(getPrimitiveTypeKind(dataType));
        return primitiveType;
    }
  }

  private PrimitiveTypeKind getPrimitiveTypeKind(GspElementDataType dataType) {
    switch (dataType) {
      case Binary:
        return PrimitiveTypeKind.Byte;
      case Boolean:
        return PrimitiveTypeKind.Bool;
      case Date:
        return PrimitiveTypeKind.DateTime;
      case DateTime:
        return PrimitiveTypeKind.DateTime;
      case Decimal:
        return PrimitiveTypeKind.Decimal;
      case Integer:
        return PrimitiveTypeKind.Int32;
      case String:
        return PrimitiveTypeKind.String;
      case Text:
        return PrimitiveTypeKind.String;
      default:
        throw CommonEntityException.createException(CommonEntityErrorCodeEnum.GSP_BEMODEL_ENUM_NOTSUPPORT_0002, dataType.toString());
    }
  }

  private String IndexString = "Index";

  private DefaultEnumerationItem convertEnumValueToItem(GspEnumValue value, String prefix,
      Boolean isIntegerIndex) {
    DefaultEnumerationItem item = new DefaultEnumerationItem();
    item.setCode(value.getValue());
    item.setDescription(value.getName());

    ObjectMapper mapper = new ObjectMapper();
    ObjectNode node = mapper.createObjectNode();
    if (isIntegerIndex) {
      node.put(IndexString, value.getIndex());
    } else {
      node.put(IndexString, value.getStringIndex());
    }

    try {
      item.setValue(mapper.writeValueAsString(node));
    } catch (JsonProcessingException e) {
      throw CommonEntityException.createException(CommonEntityErrorCodeEnum.GSP_BEMODEL_JSON_0004, e, "GspEnumValue");
    }
    return item;
  }

  private DefaultProperty convertEnumValueToProperty(String prefix, Boolean isIntegerIndex) {
    DefaultProperty prop = new DefaultProperty();
    prop.setId(String.format("%1$s-", prefix));
    prop.setCode(IndexString);
    prop.setName(IndexString);
    DefaultPrimitiveType type = new DefaultPrimitiveType();
    if (isIntegerIndex) {
      type.setPrimitiveTypeKind(PrimitiveTypeKind.Int32);
    } else {
      type.setPrimitiveTypeKind(PrimitiveTypeKind.String);
    }
    prop.setPropertyType(type);
    return prop;
  }
  // endregion

  protected String getFieldEnumTypeName(IGspCommonField field) {
    return field.getLabelID() + "Enum";
  }

  protected String getAssociationTypeName(IGspCommonField field) {
    return field.getLabelID() + "Info";
  }
}
